package com.conversationboard.filter.compression;

import java.io.IOException;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

public class CompressionResponseStream extends ServletOutputStream {

    private int minimumToCompress = 0;
    private byte[] buffer = null;
    private int count = 0;
    private GZIPOutputStream gzipOutputStream = null;
    private boolean closed = false;
    private HttpServletResponse response = null;
    private ServletOutputStream output = null;


    public CompressionResponseStream(HttpServletResponse response) throws IOException {
        super();
        this.closed = false;
        this.response = response;
        this.output = response.getOutputStream();
    }


    protected void setBuffer(int threshold) {
        this.minimumToCompress = threshold;
        this.buffer = new byte[minimumToCompress];
    }


    public void close() throws IOException {

        if (closed) {
            throw new IOException("Stream closed");
        }

        if (gzipOutputStream != null) {
            flushToGZip();
            this.gzipOutputStream.close();
            this.gzipOutputStream = null;
        } else {
            if (count > 0) {
                this.output.write(buffer, 0, count);
                this.count = 0;
            }
        }

        this.output.close();
        this.closed = true;

    }


    public void flush() throws IOException {

        if (this.closed) {
            throw new IOException("Cannot flush a closed output stream");
        }

        if (this.gzipOutputStream != null) {
            this.gzipOutputStream.flush();
        }

    }


    public void flushToGZip() throws IOException {

        if (this.count > 0) {
            writeToGZip(this.buffer, 0, this.count);
            this.count = 0;
        }

    }


    public void write(int b) throws IOException {

        if (this.closed)
            throw new IOException("Cannot write to a closed output stream");

        if (this.count >= buffer.length) {
            flushToGZip();
        }

        buffer[count++] = (byte) b;

    }


    public void write(byte b[]) throws IOException {

        write(b, 0, b.length);

    }


    public void write(byte b[], int off, int len) throws IOException {

        if (this.closed) {
            throw new IOException("Cannot write to a closed output stream");
        }

        if (len == 0) {
            return;
        }

        if (len <= (this.buffer.length - this.count)) {
            System.arraycopy(b, off, buffer, count, len);
            count += len;
            return;
        }

        flushToGZip();

        if (len <= (buffer.length - count)) {
            System.arraycopy(b, off, buffer, count, len);
            count += len;
            return;
        }

        writeToGZip(b, off, len);
    }


    public void writeToGZip(byte b[], int off, int len) throws IOException {

        if (gzipOutputStream == null) {
            response.addHeader("Content-Encoding", "gzip");
            gzipOutputStream = new GZIPOutputStream(output);
        }
        gzipOutputStream.write(b, off, len);

    }


    public boolean closed() {
        return this.closed;
    }

}
